#include <kew/c-vm.h>
#include <kew/stdlib>

/* new list implementation uses GPtrArray instead of GList to make interfacing with
   reflective features of the VM easier and more efficient */

#define kew_list_value(OBJECT) (((KewListObject *) OBJECT)->array)
#define list_reader_value(OBJECT) (((KewListReaderObject *) OBJECT)->list)
#define check_list(VAL) KewCheckType((VAL), &KewListType, "list")

KEW_METHOD(List_addLast)
{
    GPtrArray *copy = g_ptr_array_copy_add(kew_list_value(receiver), args[0]);
    results[0] = kew_wrap_list(copy);
}

KEW_METHOD(List_addLast)
{
    GPtrArray *copy = g_ptr_array_copy_add_first(kew_list_value(receiver), args[0]);
    results[0] = kew_wrap_list(copy);
}

KEW_METHOD(List_comma)
{
    check_list(args[0]);
    results[0] = kew_wrap_list(g_ptr_array_concat(kew_list_value(receiver), kew_list_value(args[0])));
}

KEW_METHOD(List_size)
{
    results[0] = kew_new_integer(g_ptr_array_size(kew_list_value(receiver)));
}

KEW_METHOD(List_containsKey)
{
    GPtrArray *array = kew_list_value(receiver)->array;    
    results[0] = kew_new_boolean(g_ptr_array_contains_index(array, KewIntVal(args[0])));
}

KEW_METHOD(List_at)
{
    int index = kew_integer_value(args[0]);
    GPtrArray *array = kew_list_value(receiver)->array;
    if (! g_ptr_array_contains_index(array, index))
        g_error("FIXME: should raise MissingKey exception here");
    results[0] = (KewObject *) g_ptr_array_get_index(array, index);
}

KEW_METHOD(List_last)
{
    GPtrArray *array = kew_list_value(receiver)->array;    
    results[0] = g_ptr_array_get_index(array, g_ptr_array_size(array) - 1);
}

KEW_METHOD(List_first)
{
    GPtrArray *array = kew_list_value(receiver)->array;    
    results[0] = g_ptr_array_get_index(array, 0);
}

KEW_METHOD(List_list)
{
    results[0] = receiver;
}

KEW_METHOD(List_reader)
{
    results[0] = kew_new_object(&list_reader_type);
    list_reader_value(results[0]) = kew_list_value(receiver);
}

/* ListReaders */

KEW_METHOD(ListReader_empty)
{
    KewListReaderObject *reader = list_reader_value(receiver);
    results[0] = kew_send11(reader->list, kew_sel_containsKey, KewNewInt(reader->index));
}

KEW_METHOD(ListReader_read)
{
    KewListReaderObject *reader = list_reader_value(receiver);
    /* FIXME: should check and raise an EmptyReader exception here instead of letting MissingKey be raised */
    results[0] = kew_send_11(reader->list, kew_sel_at, kew_new_integer(reader->index));
}


KEW_METHOD(ListReader_reader)
{
    results[0] = receiver;
}

static KewObjectType list_reader_type = {
    __FILE__, __LINE__, sizeof(KewListReaderObject), 0,
    {
        {{"read", 0, 1}, ListReader_read},
        {{"empty", 0, 1}, ListReader_empty},
        {{"reader", 0, 1}, ListReader_reader},
        {{NULL, 0, 0}, NULL}
    }
};

KewObjectType KewListType = {
    __FILE__, __LINE__, sizeof(KewListObject), 0,
    {
        {{"hash", 0, 1}, Kew__GenericHash},
        {{"=", 1, 1}, Kew__GenericEqual},
        {{"&", 1, 1}, List_addLast},
        {{"addFirst", 1, 1}, List_addFirst},
        {{"addLast", 1, 1}, List_addLast},
        {{"containsKey", 1, 1}, List_containsKey},
        {{",", 1, 1}, List_comma},
        {{"size", 0, 1}, List_size},
        {{"at", 1, 1}, List_at},
        {{"last", 0, 1}, List_last},
        {{"first", 0, 1}, List_first},
        {{"list", 0, 1}, List_list},
        {{"reader", 0, 1}, List_reader},
        {{NULL, 0, 0}, NULL}
    }
};

KewObject *
kew_wrap_list(GPtrArray *array)
{
    KewObject *result = kew_new_object(&KewListType);
    kew_list_value(result) = array;
    return result;
}

KewObject *
kew_new_list(GPtrArray *array)
{
    return kew_wrap_list(g_ptr_array_copy(array));
}

KewObject *
kew_new_list_from_array(KewObject *array[],
                        unsigned array_size)
{
    return kew_wrap_list(g_ptr_array_copy_from_array(array, size));
}

GPtrArray *
kew_kew_list_value(KewObject *list)
{
    check_list(list);
    return kew_list_value(list);
}

void
kew_intialize_list(void)
{
    kew_empty_list = kew_wrap_list(g_ptr_array_new());
}


